#include <stdexcept>

#include "../Board.h"

using namespace snake;

class Board::Impl
{
public:
  Impl(size_t width, size_t height)
    : width_(width)
    , height_(height)
  {
    if (width == 0 || height == 0)
      throw std::invalid_argument("none of dimensions can be zero");

    matrix_.resize(width * height);
  }

  size_t GetWidth() const
  {
    return width_;
  }

  size_t GetHeight() const
  {
    return height_;
  }

  bool IsCellFree(size_t x, size_t y) const
  {
    return GetCell(x, y) == CELL_EMPTY;
  }

  ECellContents GetCell(size_t x, size_t y) const
  {
    return matrix_[GetIndex(x, y)];
  }

  void SetCell(size_t x, size_t y, ECellContents contents) 
  {
    matrix_[GetIndex(x, y)] = contents;
  }

private:
  size_t GetIndex(size_t x, size_t y) const
  {
    if (x >= width_ || y >= height_)
      throw std::invalid_argument("position is out of bounds");

    return y * width_ + x;
  }

private:
  size_t width_;
  size_t height_;
  std::vector<ECellContents> matrix_;
};

Board::Board(size_t width, size_t height)
  : p_impl_(new Impl(width, height))
{
}

Board::~Board()
{
}

size_t Board::GetWidth() const 
{
  return p_impl_->GetWidth();
}

size_t Board::GetHeight() const 
{
  return p_impl_->GetHeight();
}

bool Board::IsCellFree(size_t x, size_t y) const 
{
  return p_impl_->IsCellFree(x, y);
}

ECellContents Board::GetCell(size_t x, size_t y) const
{
  return p_impl_->GetCell(x, y);
}

void Board::SetCell(size_t x, size_t y, ECellContents contents)
{
  p_impl_->SetCell(x, y, contents);
}

Board::Board(const Board &right)
  : p_impl_(new Impl(*right.p_impl_))
{
}

Board &Board::operator=(const Board &right)
{
  if (this == &right)
    return *this;

  delete p_impl_;
  p_impl_ = new Impl(*right.p_impl_);
  return *this;
}